/*
    DeaDBeeF - ultimate music player for GNU/Linux systems with X11
    Copyright (C) 2009-2012 Alexey Yakovenko <waker@users.sourceforge.net>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
#include <gtk/gtk.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "gtkui.h"
#include "support.h"
#include "ddbequalizer.h"

static GtkWidget *eqcont;
static GtkWidget *eqwin;
static GtkWidget *eqenablebtn;

static inline float
db_to_amp (float dB) {
    const float ln10=2.3025850929940002f;
    return exp(ln10*dB/20.f);
}

static inline float
amp_to_db (float amp) {
    return 20*log10 (amp);
}

ddb_dsp_context_t *
get_supereq (void) {
    ddb_dsp_context_t *dsp = deadbeef->streamer_get_dsp_chain ();
    while (dsp) {
        if (!strcmp (dsp->plugin->plugin.id, "supereq")) {
            return dsp;
        }
        dsp = dsp->next;
    }

    return NULL;
}

static void
set_param (ddb_dsp_context_t *eq, int i, float v) {
    char fv[100];
    snprintf (fv, sizeof (fv), "%f", v);
    eq->plugin->set_param (eq, i, fv);
}

void
eq_value_changed (DdbEqualizer *widget)
{
    ddb_dsp_context_t *eq = get_supereq ();
    if (eq) {
        for (int i = 0; i < 18; i++) {
            set_param (eq, i+1, ddb_equalizer_get_band (widget, i));
        }
        set_param (eq, 0, ddb_equalizer_get_preamp (widget));
        deadbeef->streamer_dsp_chain_save ();
    }
}

void
on_enable_toggled         (GtkToggleButton *togglebutton,
        gpointer         user_data) {
    ddb_dsp_context_t *eq = get_supereq ();
    if (eq) {
        int enabled = gtk_toggle_button_get_active (togglebutton) ? 1 : 0;
        eq->enabled =  enabled;
        deadbeef->streamer_dsp_refresh ();
        deadbeef->streamer_dsp_chain_save ();
    }
}

void
on_zero_all_clicked                  (GtkButton       *button,
        gpointer         user_data) {
    if (eqwin) {
        ddb_dsp_context_t *eq = get_supereq ();
        if (eq) {
            ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0);
            set_param (eq, 0, 0);
            for (int i = 0; i < 18; i++) {
                // set gui
                ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0);

                // set dsp
                set_param (eq, i+1, 0);
            }
            gtk_widget_queue_draw (eqwin);
            deadbeef->streamer_dsp_chain_save ();
        }
    }
}

void
on_zero_preamp_clicked                  (GtkButton       *button,
        gpointer         user_data) {
    if (eqwin) {
        ddb_dsp_context_t *eq = get_supereq ();
        if (eq) {
            set_param (eq, 0, 0);
            ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0);
            gtk_widget_queue_draw (eqwin);
            deadbeef->streamer_dsp_chain_save ();
        }
    }
}

void
on_zero_bands_clicked                  (GtkButton       *button,
        gpointer         user_data) {
    if (eqwin) {
        ddb_dsp_context_t *eq = get_supereq ();
        if (eq) {
            for (int i = 0; i < 18; i++) {
                ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0);
                set_param (eq, i+1, 0);
            }
            gtk_widget_queue_draw (eqwin);
            deadbeef->streamer_dsp_chain_save ();
        }
    }
}

void
on_save_preset_clicked                  (GtkMenuItem       *menuitem,
        gpointer         user_data) {
    GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Save DeaDBeeF EQ Preset"), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);

    gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dlg), TRUE);
    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dlg), "untitled.ddbeq");

    GtkFileFilter* flt;
    flt = gtk_file_filter_new ();
    gtk_file_filter_set_name (flt, _("DeaDBeeF EQ preset files (*.ddbeq)"));
    gtk_file_filter_add_pattern (flt, "*.ddbeq");

    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);

    if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_OK)
    {
        gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
        gtk_widget_destroy (dlg);

        if (fname) {
            FILE *fp = fopen (fname, "w+b");
            if (fp) {
                ddb_dsp_context_t *eq = get_supereq ();
                if (eq) {
                    char fv[100];
                    float v;
                    for (int i = 0; i < 18; i++) {
                        eq->plugin->get_param (eq, i+1, fv, sizeof (fv));
                        v = atof (fv);
                        fprintf (fp, "%f\n", v);
                    }
                    eq->plugin->get_param (eq, 0, fv, sizeof (fv));
                    v = atof (fv);
                    fprintf (fp, "%f\n", v);
                }
                fclose (fp);
            }
            g_free (fname);
        }
    }
    else {
        gtk_widget_destroy (dlg);
    }
}

void
on_load_preset_clicked                  (GtkMenuItem       *menuitem,
        gpointer         user_data) {
    GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Load DeaDBeeF EQ Preset..."), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);

    GtkFileFilter* flt;
    flt = gtk_file_filter_new ();
    gtk_file_filter_set_name (flt, _("DeaDBeeF EQ presets (*.ddbeq)"));
    gtk_file_filter_add_pattern (flt, "*.ddbeq");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);

    gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE);
    // restore folder
    deadbeef->conf_lock ();
    gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", ""));
    deadbeef->conf_unlock ();
    int response = gtk_dialog_run (GTK_DIALOG (dlg));
    // store folder
    gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
    if (folder) {
        deadbeef->conf_set_str ("filechooser.lastdir", folder);
        g_free (folder);
    }
    if (response == GTK_RESPONSE_OK)
    {
        gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
        if (fname) {
            FILE *fp = fopen (fname, "rt");
            if (fp) {
                float vals[19]; // float dBs
                int i = 0;
                while (i < 19) {
                    char tmp[20];
                    char *out = fgets (tmp, sizeof (tmp), fp);
                    if (!out) {
                        break;
                    }
                    vals[i] = atof (tmp);
                    i++;
                }
                fclose (fp);
                if (i == 19) {
                    // apply and save config
                    ddb_dsp_context_t *eq = get_supereq ();
                    if (eq) {
                        set_param (eq, 0, vals[18]);
                        ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), vals[18]);
                        for (int i = 0; i < 18; i++) {
                            ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, vals[i]);
                            set_param (eq, i+1, vals[i]);
                        }
                        gtk_widget_queue_draw (eqwin);
                        deadbeef->streamer_dsp_chain_save ();
                    }
                }
                else {
                    fprintf (stderr, "[eq] corrupted DeaDBeeF preset file, discarded\n");
                }
            }
            g_free (fname);
        }
    }
    gtk_widget_destroy (dlg);
}

void
on_import_fb2k_preset_clicked                  (GtkButton       *button,
        gpointer         user_data) {
    GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Import Foobar2000 EQ Preset..."), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);

    GtkFileFilter* flt;
    flt = gtk_file_filter_new ();
    gtk_file_filter_set_name (flt, _("Foobar2000 EQ presets (*.feq)"));
    gtk_file_filter_add_pattern (flt, "*.feq");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);

    gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE);
    // restore folder
    deadbeef->conf_lock ();
    gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", ""));
    deadbeef->conf_unlock ();

    int response = gtk_dialog_run (GTK_DIALOG (dlg));
    // store folder
    gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
    if (folder) {
        deadbeef->conf_set_str ("filechooser.lastdir", folder);
        g_free (folder);
    }
    if (response == GTK_RESPONSE_OK)
    {
        gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
        if (fname) {
            FILE *fp = fopen (fname, "rt");
            if (fp) {
                int vals[18]; // integer dBs
                int i = 0;
                while (i < 18) {
                    char tmp[20];
                    char *out = fgets (tmp, sizeof (tmp), fp);
                    if (!out) {
                        break;
                    }
                    vals[i] = atoi (tmp);
                    i++;
                }
                fclose (fp);
                if (i == 18) {
                    // apply and save config
                    ddb_dsp_context_t *eq = get_supereq ();
                    if (eq) {
                        set_param (eq, 0, 0);
                        ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0);
                        for (int i = 0; i < 18; i++) {
                            ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, vals[i]);
                            set_param (eq, i+1, vals[i]);
                        }
                        gtk_widget_queue_draw (eqwin);
                        deadbeef->streamer_dsp_chain_save ();
                    }
                }
                else {
                    fprintf (stderr, "[eq] corrupted Foobar2000 preset file, discarded\n");
                }
            }
            g_free (fname);
        }
    }
    gtk_widget_destroy (dlg);
}

void
on_presets_clicked                  (GtkButton       *button,
        gpointer         user_data) {
    GtkWidget *menu = gtk_menu_new ();
    GtkWidget *menuitem;

    menuitem = gtk_menu_item_new_with_mnemonic (_("Save Preset"));
    gtk_widget_show (menuitem);
    gtk_container_add (GTK_CONTAINER (menu), menuitem);

    g_signal_connect ((gpointer) menuitem, "activate",
            G_CALLBACK (on_save_preset_clicked),
            NULL);

    menuitem = gtk_menu_item_new_with_mnemonic (_("Load Preset"));
    gtk_widget_show (menuitem);
    gtk_container_add (GTK_CONTAINER (menu), menuitem);

    g_signal_connect ((gpointer) menuitem, "activate",
            G_CALLBACK (on_load_preset_clicked),
            NULL);

    menuitem = gtk_menu_item_new_with_mnemonic (_("Import Foobar2000 Preset"));
    gtk_widget_show (menuitem);
    gtk_container_add (GTK_CONTAINER (menu), menuitem);

    g_signal_connect ((gpointer) menuitem, "activate",
            G_CALLBACK (on_import_fb2k_preset_clicked),
            NULL);

    gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
}

void
eq_window_show (void) {
    if (!eqcont) {
        eqcont = gtk_vbox_new (FALSE, 8);
        GtkWidget *parent= lookup_widget (mainwin, "plugins_bottom_vbox");
        gtk_box_pack_start (GTK_BOX (parent), eqcont, FALSE, FALSE, 0);

        GtkWidget *buttons = gtk_hbox_new (FALSE, 8);
        gtk_container_set_border_width (GTK_CONTAINER (buttons), 3);
        gtk_widget_show (buttons);
        gtk_box_pack_start (GTK_BOX (eqcont), buttons, FALSE, FALSE, 0);

        GtkWidget *button;

        eqenablebtn = button = gtk_check_button_new_with_label (_("Enable"));
        gtk_widget_show (button);
        gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
        ddb_dsp_context_t *eq = get_supereq ();
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eqenablebtn), eq ? eq->enabled : 0);
        g_signal_connect ((gpointer) button, "toggled",
                G_CALLBACK (on_enable_toggled),
                NULL);

        button = gtk_button_new_with_label (_("Zero All"));
        gtk_widget_show (button);
        gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
        g_signal_connect ((gpointer) button, "clicked",
                G_CALLBACK (on_zero_all_clicked),
                NULL);

        button = gtk_button_new_with_label (_("Zero Preamp"));
        gtk_widget_show (button);
        gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
        g_signal_connect ((gpointer) button, "clicked",
                G_CALLBACK (on_zero_preamp_clicked),
                NULL);

        button = gtk_button_new_with_label (_("Zero Bands"));
        gtk_widget_show (button);
        gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
        g_signal_connect ((gpointer) button, "clicked",
                G_CALLBACK (on_zero_bands_clicked),
                NULL);

        button = gtk_button_new_with_label (_("Presets"));
        gtk_widget_show (button);
        gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
        g_signal_connect ((gpointer) button, "clicked",
                G_CALLBACK (on_presets_clicked),
                NULL);

        eqwin = GTK_WIDGET (ddb_equalizer_new());
        g_signal_connect (eqwin, "on_changed", G_CALLBACK (eq_value_changed), 0);
        gtk_widget_set_size_request (eqwin, -1, 200);

        if (eq) {
            char fv[100];
            float v;
            eq->plugin->get_param (eq, 0, fv, sizeof (fv));
            v = atof (fv);
            ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), v);
            for (int i = 0; i < 18; i++) {
                if (eq) {
                    eq->plugin->get_param (eq, i+1, fv, sizeof (fv));
                    v = atof (fv);
                    ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, v);
                }
            }
        }

        gtk_widget_show (eqwin);
        gtk_box_pack_start (GTK_BOX (eqcont), eqwin, TRUE, TRUE, 0);
    }
    gtk_widget_show (eqcont);
}

void
eq_window_hide (void) {
    if (eqcont) {
        gtk_widget_hide (eqcont);
    }
}

void
eq_window_destroy (void) {
    if (eqwin) {
        gtk_widget_destroy (eqwin);
        eqwin = NULL;
    }
}

void
eq_redraw (void) {
    if (eqwin) {
        gtk_widget_queue_draw (eqwin);
    }
}

void
eq_refresh (void) {
    ddb_dsp_context_t *eq = get_supereq ();
    if (eq && eqwin) {
        char s[20];
        eq->plugin->get_param (eq, 0, s, sizeof (s));
        ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), atof(s));
        for (int i = 0; i < 18; i++) {
            eq->plugin->get_param (eq, i+1, s, sizeof (s));
            ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, atoi(s));
        }
        eq_redraw ();
    }
}
